﻿<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>View Components </title>
    <meta name="viewport" content="width=device-width">
    <meta name="title" content="View Components ">
    <meta name="generator" content="docfx 2.45.1.0">
    
    <link rel="shortcut icon" href="../favicon.ico">
    <link rel="stylesheet" href="../styles/docfx.vendor.css">
    <link rel="stylesheet" href="../styles/docfx.css">
    <link rel="stylesheet" href="../styles/main.css">
    <meta property="docfx:navrel" content="">
    <meta property="docfx:tocrel" content="toc.html">
    <meta property="docfx:rel" content="../">
    <meta property="og:title" content="Flexible and asynchronous testing framework for ASP.NET Core MVC">
    <meta property="og:site_name" content="My Tested ASP.NET Core MVC Docs">
    <meta property="og:url" content="http://docs.mytestedasp.net/">
    <meta property="og:description" content="A fluent unit testing library for ASP.NET Core MVC">
    <meta property="og:image" content="https://mytestedasp.net/Content/Images/logosocial.jpg">
    <meta property="og:type" content="website">
    <meta property="og:locale" content="en_US">
    <meta property="twitter:card" content="summary">
    <meta property="twitter:title" content="Flexible and asynchronous testing framework for ASP.NET Core MVC">
    <meta property="twitter:description" content="A fluent unit testing library for ASP.NET Core MVC">
    <meta property="twitter:creator" content="@MyTestedASPNET">
    <meta property="twitter:url" content="https://mytestedasp.net/">
    <meta property="twitter:image" content="https://mytestedasp.net/Content/Images/logosocial.jpg">
  </head>
  <body data-spy="scroll" data-target="#affix" data-offset="120">
    <div id="wrapper">
      <header>
        <nav id="autocollapse" class="navbar navbar-inverse ng-scope" role="navigation">
          <div class="container">
            <div class="navbar-header">
              <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="../index.html">
                MY TESTED ASP.NET CORE MVC DOCS
              </a>
            </div>
            <div class="collapse navbar-collapse" id="navbar">
              <form class="navbar-form navbar-right" role="search" id="search">
                <div class="form-group">
                  <input type="text" class="form-control" id="search-query" placeholder="Search" autocomplete="off">
                </div>
              </form>
            </div>
          </div>
        </nav>
        
        <div class="subnav navbar navbar-default">
          <div class="container hide-when-search" id="breadcrumb">
            <ul class="breadcrumb">
              <li></li>
            </ul>
          </div>
        </div>
      </header>
      <div role="main" class="container body-content hide-when-search">
        
        <div class="sidenav hide-when-search">
          <a class="btn toc-toggle collapse" data-toggle="collapse" href="#sidetoggle" aria-expanded="false" aria-controls="sidetoggle">Show / Hide Table of Contents</a>
          <div class="sidetoggle collapse" id="sidetoggle">
            <div id="sidetoc"></div>
          </div>
        </div>
        <div class="article row grid-right">
          <div class="col-md-10">
            <article class="content wrap" id="_content" data-uid="">
<h1 id="view-components">View Components</h1>

<p>In this section we are going to stop asserting controllers (finally) and concentrate on <strong>&quot;View Components&quot;</strong>. Typical MVC application written with separation of concerns in mind should have plenty of them. Before we begin, we need the <strong>&quot;MyTested.AspNetCore.Mvc.ViewComponents&quot;</strong> package:</p>
<pre><code class="lang-xml">&lt;!-- Other ItemGroups --&gt;

  &lt;ItemGroup&gt;
    &lt;PackageReference Include=&quot;Microsoft.AspNetCore.App&quot; /&gt;
    &lt;PackageReference Include=&quot;Microsoft.NET.Test.Sdk&quot; Version=&quot;16.0.1&quot; /&gt;
    &lt;PackageReference Include=&quot;Moq&quot; Version=&quot;4.13.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Authentication&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Caching&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Controllers&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Controllers.ActionResults&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Controllers.Attributes&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Controllers.Views&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Controllers.Views.ActionResults&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.DependencyInjection&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.EntityFrameworkCore&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Http&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Models&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.ModelState&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Options&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.Session&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;!-- MyTested.AspNetCore.Mvc.ViewComponents package --&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.ViewComponents&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;MyTested.AspNetCore.Mvc.ViewData&quot; Version=&quot;2.2.0&quot; /&gt;
    &lt;PackageReference Include=&quot;xunit&quot; Version=&quot;2.4.0&quot; /&gt;
    &lt;PackageReference Include=&quot;xunit.runner.visualstudio&quot; Version=&quot;2.4.0&quot; /&gt;
  &lt;/ItemGroup&gt;

&lt;!-- Other ItemGroups --&gt;
</code></pre><p>Then we need to create a <strong>&quot;ViewComponents&quot;</strong> folder at the root of our test project and add <strong>&quot;CartSummaryComponentTest&quot;</strong> class in it. We are going to assert the <strong>&quot;CartSummaryComponent&quot;</strong>.</p>
<h2 id="arrange">Arrange</h2>
<p>Arranging the test is done in the same manner as it is with controllers. This is the code we want to test:</p>
<pre><code class="lang-c#">private MusicStoreContext DbContext { get; }

public async Task&lt;IViewComponentResult&gt; InvokeAsync()
{
    var cart = ShoppingCart.GetCart(DbContext, HttpContext);

    var cartItems = await cart.GetCartAlbumTitles();

    ViewBag.CartCount = cartItems.Count;
    ViewBag.CartSummary = string.Join(&quot;\n&quot;, cartItems.Distinct());

    return View();
}
</code></pre><p>We need session cart ID and items in the database for the shopping cart. Add a <strong>&quot;CartItemData&quot;</strong> class in the <strong>&quot;Data&quot;</strong> folder and write these lines in it:</p>
<pre><code class="lang-c#">using MusicStore.Models;
using System.Collections.Generic;
using System.Linq;

public class CartItemData
{
    public static IEnumerable&lt;CartItem&gt; GetMany(string cartId, string albumTitle)
    {
        var album = new Album { AlbumId = 1, Title = albumTitle };

        return Enumerable
            .Range(1, 10)
            .Select(n =&gt; new CartItem
            {
                AlbumId = 1,
                Album = album,
                Count = 1,
                CartId = cartId,
            });
    }
}
</code></pre><p>Starting the test is done from the <strong>&quot;MyViewComponent&quot;</strong> test:</p>
<pre><code class="lang-c#">[Fact]
public void InvokingTheComponentShouldReturnCorrectCartItems()
    =&gt; MyViewComponent&lt;CartSummaryComponent&gt;
        .Instance()
</code></pre><p>We need to add a cart ID in the session state:</p>
<pre><code class="lang-c#">MyViewComponent&lt;CartSummaryComponent&gt;
    .Instance()
    .WithSession(session =&gt; session // &lt;---
        .WithEntry(&quot;Session&quot;, &quot;TestCart&quot;))
</code></pre><p>And database entities:</p>
<pre><code class="lang-c#">MyViewComponent&lt;CartSummaryComponent&gt;
    .Instance()
    .WithSession(session =&gt; session
        .WithEntry(&quot;Session&quot;, &quot;TestCart&quot;))
    .WithData(CartItemData.GetMany(&quot;TestCart&quot;, &quot;TestAlbum&quot;)) // &lt;---
</code></pre><h2 id="act">Act</h2>
<p>After arranging all objects we need for the view component, we have to invoke it by using the <strong>&quot;InvokedWith&quot;</strong> method:</p>
<pre><code class="lang-c#">MyViewComponent&lt;CartSummaryComponent&gt;
    .Instance()
    .WithSession(session =&gt; session
        .WithEntry(&quot;Session&quot;, &quot;TestCart&quot;))
    .WithData(db =&gt; db
        .WithEntities(entities =&gt; entities
            .AddRange(GetCartItems(&quot;TestCart&quot;, &quot;TestAlbum&quot;))))
    .InvokedWith(vc =&gt; vc.InvokeAsync()) // &lt;---
</code></pre><h2 id="assert">Assert</h2>
<p>Finally, we need to assert the <strong>&quot;ViewBag&quot;</strong> and the execution result. You already now how to do it:</p>
<pre><code class="lang-c#">MyViewComponent&lt;CartSummaryComponent&gt;
    .Instance()
    .WithSession(session =&gt; session
        .WithEntry(&quot;Session&quot;, &quot;TestCart&quot;))
    .WithData(db =&gt; db
        .WithEntities(entities =&gt; entities
            .AddRange(GetCartItems(&quot;TestCart&quot;, &quot;TestAlbum&quot;))))
    .InvokedWith(vc =&gt; vc.InvokeAsync())
    .ShouldHave() // &lt;---
    .ViewBag(viewBag =&gt; viewBag
        .ContainingEntries(new
        {
            CartCount = 10,
            CartSummary = &quot;TestAlbum&quot;
        }))
    .AndAlso()
    .ShouldReturn() // &lt;---
    .View();
</code></pre><p>Rebuild the project and run the test to see it pass! :)</p>
<p>As with all tests with My Tested ASP.NET Core MVC, if you have an invalid value, you will receive a friendly error message. Change <strong>&quot;Session&quot;</strong> to <strong>&quot;Cache&quot;</strong> and see for yourself:</p>
<pre><code class="lang-text">When invoking CartSummaryComponent expected view bag to have entry with &#39;CartCount&#39; key and the provided value, but the value was different.
</code></pre><h2 id="section-summary">Section summary</h2>
<p>View component testing provides the same API methods as the controller one as long as they are applicable. You should be seeing the big picture of the library now. The next section is interesting - the good old <a href="/tutorial/routing.html">Route</a> testing!</p>
</article>
          </div>
          
          <div class="hidden-sm col-md-2" role="complementary">
            <div class="sideaffix">
              <div class="contribution">
                <ul class="nav">
                  <li>
                    <a href="https://github.com/tmollov/MyTested.AspNetCore.Mvc/blob/development/docs/_docfx/tutorial/viewcomponents.md/#L1" class="contribution-link">Improve this Doc</a>
                  </li>
                </ul>
              </div>
              <nav class="bs-docs-sidebar hidden-print hidden-xs hidden-sm affix" id="affix">
              <!-- <p><a class="back-to-top" href="#top">Back to top</a><p> -->
              </nav>
            </div>
          </div>
        </div>
      </div>
      <footer>
        <div class="grad-bottom"></div>
        <div class="footer">
          <div class="container">
            <span class="pull-right">
              <a href="#top">Back to top</a>
            </span>
            <span>Copyright © 2015-2016 <strong><a href="http://mytestedasp.net">MyTestedASP.NET</a></strong>. All Rights Reserved. Generated by <strong><a href="http://dotnet.github.io/docfx/">DocFX</a></strong></span>
          </div>
        </div>
      </footer>
    </div>
    <script type="text/javascript" src="../styles/docfx.vendor.js"></script>
    <script type="text/javascript" src="../styles/docfx.js"></script>
    <script type="text/javascript" src="../styles/main.js"></script>
    <script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
      ga('create', 'UA-51720358-4', 'auto');
      ga('send', 'pageview');
    </script>
    <script>
    !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
    n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
    n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
    t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
    document,'script','https://connect.facebook.net/en_US/fbevents.js');
    fbq('init', '884740311601716');
    fbq('track', 'PageView');
    </script>
    <noscript><img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=884740311601716&ev=PageView&noscript=1"></noscript>  </body>
</html>
